/*************************************************************************
	> File Name: threadPool.hpp
	> Author: H.M. Lau
	> Mail: hm.lau@outlook.com
	> Created Time: 2023/10/04 18:58
 ************************************************************************/

#pragma once

#include <vector>
#include <queue>
#include <semaphore.h>
#include "thread.hpp"
#include "log.hpp"
#include "lockGuard.hpp"

const int gThreadNum = 10;

template<class T>
class ThreadPool
{
public:
    static ThreadPool<T>* getThreadPool(int num = gThreadNum)
    {
        if (!_tp_ptr) {
            LockGuard lockGuard(&_mtx);
            if (!_tp_ptr) {
                std::cout << 2 << std::endl;
                _tp_ptr = new ThreadPool<T>(num);
            }
        }
        return _tp_ptr;
    }

    void run()
    {
        for (const auto& t : _threads) {
            t->start();
            logMessage(NORMAL, "%s set up", t->name().c_str());
        }
    }

    void pushTask(const T& t)
    {
        LockGuard lockGuard(&_lock);
        _tq.push(t);
        pthread_cond_signal(&_cond);
    }
    
private:
    ThreadPool (int threadNum = gThreadNum):_num(threadNum)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; ++i) {
            _threads.push_back(new Thread(i, routine, this));
        }
    }

    virtual ~ThreadPool ()
    {
        for (const auto& t : _threads) {
            t->join();
            delete t;
        }
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }

    ThreadPool(const ThreadPool<T>& other) = delete;

    const ThreadPool<T>& operator = (const ThreadPool<T>& other) = delete;

    pthread_mutex_t* getMutex()
    {
        return &_mtx;
    }

    void waitCond()
    {
        pthread_cond_wait(&_cond, &_lock);
    }

    T getTask()
    {
        T t = _tq.front();
        _tq.pop();
        return t;
    }


    static void* routine(void* args)
    {
        ThreadData* td = static_cast<ThreadData*>(args);
        ThreadPool<T>* tp = static_cast<ThreadPool<T>*>(td->_args);
        while (1) {
            T task;
            {
                LockGuard lockGuard(tp->getMutex());
                while (tp->isEmpty()) {
                    tp->waitCond();
                }
                task = tp->getTask();
            }
            task();
        }
    }

    bool isEmpty()
    {
        return _tq.empty();
    }

    static ThreadPool<T>* _tp_ptr;
    pthread_mutex_t _lock;
    static pthread_mutex_t _mtx;
    pthread_cond_t _cond;
    int _num;
    std::vector<Thread*> _threads;
    std::queue<T> _tq;
};
template<class T>
ThreadPool<T>* ThreadPool<T>::_tp_ptr = nullptr;

template<class T>
pthread_mutex_t ThreadPool<T>::_mtx = PTHREAD_MUTEX_INITIALIZER;
